//------------------------------------------------------------------------------
// RegFolder.cpp
//------------------------------------------------------------------------------
// File provided for Microsoft Shared Source.
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE.
//------------------------------------------------------------------------------
//
// Shell folder implementation for the registry shell extension
//
//------------------------------------------------------------------------------

// to debug this under a new explorer instance setup the following debugging info
// Command: "explorer.exe"
// Command Arguments: "::{E48ABDCD-0DE7-4C5A-91EB-13D6E81506F0}"

#include "pch.h"
#include <shidfact.h>
#include <propkeydef.h>
#include <propkey.h>
#include <Utils.h>
#include <RegNamespace.h>

#define REG_KEY ((UINT)99)

typedef CStackString<1024> CRegistryPathString;

//------------------------------------------------------------------------------
// CRegFolder
//------------------------------------------------------------------------------

enum REGITEMTYPE
{
    RIT_NAMESPACEROOT = 1,  // indicates the root of the registry namespace
    RIT_ROOTKEY,            // root registry key
    RIT_SUBKEY,             // sub key
    RIT_VALUE               // registry value
};

#define REGITEM_MAGIC 0x2a573c37
struct REGITEM
{
    REGITEMTYPE rit;
    HKEY hkey;
};
typedef UNALIGNED REGITEM *PREGITEM;
typedef const UNALIGNED REGITEM *PCREGITEM;

// shell data source object

class ATL_NO_VTABLE CRegFolder :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CRegFolder, &CLSID_RegFolder>,
    public IObjectWithSiteImpl<CRegFolder>,
    public CItemIDFactory<REGITEM, REGITEM_MAGIC>,
    public IPersistFolder2,
    public IShellFolder2,
    public IRegPropertyHandler,
    public IContextMenuCB
{
public:
    CRegFolder() : _hkey(0)
    {
    }

    DECLARE_REGISTRY_RESOURCEID(IDR_REGFOLDER)

    BEGIN_COM_MAP(CRegFolder)
        COM_INTERFACE_ENTRY2(IPersist, IPersistFolder2)
        COM_INTERFACE_ENTRY2(IPersistFolder, IPersistFolder2)
        COM_INTERFACE_ENTRY(IPersistFolder2)
        COM_INTERFACE_ENTRY(IPersistFolder2)
        COM_INTERFACE_ENTRY2(IShellFolder, IShellFolder2)
        COM_INTERFACE_ENTRY(IShellFolder2)
        COM_INTERFACE_ENTRY(IRegPropertyHandler)
        COM_INTERFACE_ENTRY(IContextMenuCB)
        COM_INTERFACE_ENTRY(IObjectWithSite)
    END_COM_MAP()

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct()
    {
        return S_OK;
    }

    void FinalRelease()
    {
    }

    BOOL IsRoot() { return _hkey == 0; }

    // IPersist
    IFACEMETHODIMP GetClassID(__out CLSID *pclsid);

    // IPersistFolder
    IFACEMETHODIMP Initialize(PCIDLIST_ABSOLUTE pidl);

    // IPersistFolder2
    IFACEMETHODIMP GetCurFolder(__out PIDLIST_ABSOLUTE *ppidl);

    // IShellFolder
    IFACEMETHODIMP ParseDisplayName(HWND hwnd, __in IBindCtx *pbc, __in PWSTR pszDisplayName, __inout ULONG *pchEaten, __deref_out PIDLIST_RELATIVE *ppidl, __inout ULONG *pdwAttributes);
    IFACEMETHODIMP EnumObjects(HWND hwnd, SHCONTF grfFlags, __deref_out IEnumIDList **ppenmIDList);
    IFACEMETHODIMP BindToObject(PCUIDLIST_RELATIVE pidl, __in IBindCtx *pbc, REFIID riid, __deref_out void **ppv);
    IFACEMETHODIMP BindToStorage(PCUIDLIST_RELATIVE pidl, __in IBindCtx *pbc, REFIID riid, __deref_out void **ppv);
    IFACEMETHODIMP CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2);
    IFACEMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, __deref_out void **ppv);
    IFACEMETHODIMP GetAttributesOf(UINT cidl, __in_ecount_opt(cidl) PCUITEMID_CHILD_ARRAY rgpidl, __inout SFGAOF *rgfInOut);
    IFACEMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, __in_ecount_opt(cidl) PCUITEMID_CHILD_ARRAY rgpidl, REFIID riid, __reserved UINT *rgfReserved, __deref_out void **ppv);
    IFACEMETHODIMP GetDisplayNameOf(PCUITEMID_CHILD pidl, SHGDNF uFlags, __out STRRET *psrName);
    IFACEMETHODIMP SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCWSTR pszName, SHGDNF uFlags, __deref_out_opt PITEMID_CHILD *ppidlOut);

    // IShellFolder2
    IFACEMETHODIMP GetDefaultSearchGUID(__out GUID *pguid);
    IFACEMETHODIMP EnumSearches(__deref_out IEnumExtraSearch **ppenum);
    IFACEMETHODIMP GetDefaultColumn(DWORD dwRes, __out ULONG *plSort, __out ULONG *plDisplay);
    IFACEMETHODIMP GetDefaultColumnState(UINT iColumn, __out SHCOLSTATEF *pcsFlags);
    IFACEMETHODIMP GetDetailsEx(PCUITEMID_CHILD pidl, const PROPERTYKEY *pkey, __out VARIANT *pvar);
    IFACEMETHODIMP GetDetailsOf(__in_opt PCUITEMID_CHILD pidl, UINT iColumn, __out SHELLDETAILS *psd);
    IFACEMETHODIMP MapColumnToSCID(UINT iColumn, __out PROPERTYKEY *pkey);

    // IRegPropertyHandler
    IFACEMETHODIMP GetProperty(PCUITEMID_CHILD pidl, REFPROPERTYKEY key, __out PROPVARIANT *ppropvar);
    IFACEMETHODIMP SetProperty(PCUITEMID_CHILD pidl, REFPROPERTYKEY key, REFPROPVARIANT propvar);

    // IContextMenuCB
    IFACEMETHODIMP CallBack(__in_opt IShellFolder *psf, HWND hwndOwner, __in_opt IDataObject *pdtobj, UINT uiMsg, WPARAM wParam, LPARAM lParam);

private:
    HRESULT _CreateFolder(PCUITEMID_CHILD pidlChild, REFIID riid, __deref_out void **ppv);
    HRESULT _Initialize(PCIDLIST_ABSOLUTE pidl, PCUITEMID_CHILD pidlChild, HKEY hkey, PCWSTR pszPath);
    HRESULT _CreateItemID(REGITEM *pri, PCWSTR pszRegName, DWORD dwType, __deref_out PITEMID_CHILD *ppidl);
    
    HRESULT _NextNameSegment(__inout PWSTR *ppszInOut, __deref_out PWSTR *ppszSegment, __out REGITEM *pri, __out DWORD *pdwType);
    HRESULT _MapStringToHKEY(PCWSTR pszKey, __out HKEY *phkey);
    HRESULT _GetAbsoluteName(SIGDN sigdn, PCUITEMID_CHILD pidl, __deref_out PWSTR *ppszName);
    HRESULT _GetNameDisplay(PCUITEMID_CHILD pidl, __deref_out PWSTR *ppszName);
    HRESULT _GetAbsoluteFriendlyName(PCUITEMID_CHILD pidl, __deref_out PWSTR *ppszName);
    HRESULT _GetParsingName(PCUITEMID_CHILD pidl, __deref_out PWSTR *ppszName);
    HRESULT _GetAbsoluteParsingName(PCUITEMID_CHILD pidl, __deref_out PWSTR *ppszName);
    HRESULT _ValidateParsedIDList(PCUITEMID_CHILD pidl);
    HRESULT _FindRootRegItem(PCUIDLIST_ABSOLUTE pidlFull, __out PCUIDLIST_RELATIVE *ppidl);
    HRESULT _CompareRelativeIDLists(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2);
    HRESULT _ShellDetailsFromPropDesc(__in IPropertyDescription *ppropdesc, __out SHELLDETAILS *psd);
    HRESULT _GetListViewFlagsFromPropDesc(__in IPropertyDescription *ppropdesc, __out int *pfmt);
    HRESULT _ColumnStateFromPropDesc(__in IPropertyDescription *ppropdesc, __out SHCOLSTATEF *pcsFlags);
    HRESULT _GetAttributes(PCUITEMID_CHILD pidl, DWORD rgfIn, __out DWORD *prgfOut);
    HRESULT _HasSubfolders(PCUITEMID_CHILD pidl, __out BOOL *pfHasSubfolders);
    HRESULT _CreateChildIDListArray(SHCONTF grfFlags, __inout CAtlArray<PITEMID_CHILD> &rgItems);
    HRESULT _IsValue(PCUIDLIST_RELATIVE pidl, __out BOOL *pfIsValue);
    HRESULT _RegNameFromParsingName(PCWSTR pszParsingName, __out_opt REGITEMTYPE *prit, __deref_out PWSTR *ppszRegName);
    HRESULT _ParsingNameFromRegName(PCWSTR pszRegName, REGITEMTYPE rit, __deref_out PWSTR *ppszParsingName);

    // Property helpers
    static const struct COLUMNINFO
    {
        REFPROPERTYKEY key;
        SHCOLSTATEF scsf;
        HRESULT (CRegFolder::*pfnGetProperty)(PCUITEMID_CHILD, PROPVARIANT *);
    } c_rgColumnInfo[];

    HRESULT _GetParsingName(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);
    HRESULT _GetDisplayName(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);    
    HRESULT _GetAbsoluteDisplayName(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);
    HRESULT _GetItemFolderPathDisplay(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);
    HRESULT _GetItemFolderPathDisplayNarrow(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);
    HRESULT _GetRegistryType(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);
    HRESULT _GetRegistryTypeDisplay(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);
    HRESULT _GetRegistryData(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);
    HRESULT _GetRegistryDataDisplay(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);
    HRESULT _GetDateModified(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);
    HRESULT _GetUIPropList(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);
    HRESULT _GetFileAttributes(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar);
    HRESULT _BinaryToText(const BYTE *pblob, DWORD cb, __out PWSTR *ppszText);
    HRESULT _GetPropertyForItem(PCUITEMID_CHILD pidl, REFPROPERTYKEY key, __out PROPVARIANT *ppropvar);
    HRESULT _GetKeyPathForItem(__in PCUITEMID_CHILD pidl, __out HKEY *pkey, __inout CRegistryPathString *pstrKey);
    HRESULT _OpenKeyForItem(PCUITEMID_CHILD pidl, DWORD dwAccess, __out CRegKey *prk);
    HRESULT _OpenKeyForFolder(DWORD dwAccess, __out CRegKey *prk);

    // Context menu helpers
    struct CMVERBTOIDMAP
    {
        PCWSTR pszVerb;
        const char *pszVerbA;
        UINT uiCmdId;
        UINT uiHelpId;
    };
    HRESULT _HandleStandardMenuMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam, const CMVERBTOIDMAP *pmap, UINT cVerbs);
    HRESULT _CmdIDToMap(UINT_PTR uiCmdId, BOOL fUnicode, const CMVERBTOIDMAP *pmap, UINT cVerbs, __out const CMVERBTOIDMAP **ppmapMatch);
    HRESULT _MapVerbToCmdID(PCWSTR pszVerb, const CMVERBTOIDMAP *pmap, UINT cVerbs, __out UINT *puiCmdId);
    HRESULT _MapCmdIDToVerb(UINT_PTR uiCmdId, UINT uiFlags, const CMVERBTOIDMAP *pmap, UINT cVerbs, __out_awcount(!(uiFlags & GCS_UNICODE), cch) LPSTR pszName, UINT cch);
    HRESULT _MapCmdIDToHelpText(UINT_PTR uiCmdId, UINT uiFlags, const CMVERBTOIDMAP *pmap, UINT cVerbs, __out_awcount(!(uiFlags & GCS_UNICODE), cch) LPSTR pszName, UINT cch);
    HRESULT _ValidateCmdID(UINT_PTR uiCmdId, UINT uiFlags, const CMVERBTOIDMAP *pmap, UINT cVerbs);

    // folder view callback object, used to customize the view behavior of a data source instance

    class CViewCallback : 
        public CComObjectRootEx<CComSingleThreadModel>, 
        public IShellFolderViewCB,
        public IFolderViewSettings
    {
    public:
        CViewCallback()
        {
        }

        BEGIN_COM_MAP(CViewCallback)
            COM_INTERFACE_ENTRY(IShellFolderViewCB)
            COM_INTERFACE_ENTRY(IFolderViewSettings)
        END_COM_MAP()

        DECLARE_PROTECT_FINAL_CONSTRUCT()

        static HRESULT s_CreateInstance(CRegFolder *prf, REFIID riid, __deref_out void **ppv)
        {
            *ppv = NULL;
            CComObjectWithRef<CViewCallback> *psprfvcb;
            HRESULT hr = CComObjectWithRef<CViewCallback>::CreateInstance(&psprfvcb);
            if (SUCCEEDED(hr))
            {
                hr = psprfvcb->_Initialize(prf);
                if (SUCCEEDED(hr))
                {
                    hr = psprfvcb->QueryInterface(riid, ppv);
                }
                psprfvcb->Release();
            }
            return hr;
        }

        HRESULT FinalConstruct()
        {
            return S_OK;
        }

        void FinalRelease()
        {
            if (_prf)
            {
                _prf->Release();
            }
        }

        // IShellFolderViewCB
        IFACEMETHODIMP MessageSFVCB(UINT uiMsg, WPARAM wParam, LPARAM lParam)
        {
            HRESULT hr;
            switch (uiMsg)
            {
            case SFVM_GETNOTIFY:
                *(PCIDLIST_ABSOLUTE *)wParam = (PIDLIST_ABSOLUTE)_spidl;  // evil weak reference
                *(LONG *)lParam = SHCNE_UPDATEITEM;
                hr = S_OK;

            default:
                hr = E_FAIL;
            }
            return hr;
        }

        // IFolderViewSettings
        IFACEMETHODIMP GetColumnPropertyList(__in REFIID riid, __deref_out_opt void **ppv)
        {
            *ppv = NULL;
            return E_NOTIMPL;
        }
        
        IFACEMETHODIMP GetGroupByProperty(__out PROPERTYKEY *pkey, __out BOOL *pfGroupAscending)
        {
            return E_NOTIMPL;
        }

        IFACEMETHODIMP GetViewMode(__out FOLDERLOGICALVIEWMODE *plvm)
        {
            *plvm = _prf->IsRoot() ? FLVM_TILES : FLVM_DETAILS;
            return S_OK;
        }

        IFACEMETHODIMP GetIconSize(__out UINT *puIconSize)
        {
            return E_NOTIMPL;
        }
        
        IFACEMETHODIMP GetFolderFlags(__out FOLDERFLAGS *pfolderMask, __out FOLDERFLAGS *pfolderFlags)
        { 
            // do a deep search when word wheeling
            // for some unknown reason this does not work, debug this later
            *pfolderMask = FWF_USESEARCHFOLDER; 
            *pfolderFlags = FWF_USESEARCHFOLDER; 
            return S_OK; // E_NOTIMPL;
        }

        IFACEMETHODIMP GetSortColumns(__out_ecount_part(cColumnsIn, *pcColumnsOut) SORTCOLUMN *rgSortColumns, UINT cColumnsIn, __out UINT *pcColumnsOut)
        {
            return E_NOTIMPL;
        }
        
        IFACEMETHODIMP GetGroupSubsetCount(__out UINT *pcVisibleRows)
        {
            return E_NOTIMPL;
        }

    private:
        HRESULT _Initialize(CRegFolder *prf)
        {
            HRESULT hr = prf->GetCurFolder(&_spidl);
            if (SUCCEEDED(hr))
            {
                _prf = prf;
                _prf->AddRef();
            }
            return hr;
        }

    private:
        CComMemPtr<ITEMIDLIST_ABSOLUTE> _spidl;
        CRegFolder *_prf;
    };

private:
    CComMemPtr<ITEMIDLIST_ABSOLUTE> _spidl;  // namespace location
    HKEY _hkey;                                 // root registry key (HKEY_CURENT_USER, HKEY_LOCAL_MACHINE, etc. 0 for namespace root
    CComMemPtr<WCHAR> _spszPath;             // registry key path
};

OBJECT_ENTRY_AUTO(__uuidof(RegFolder), CRegFolder)

const CRegFolder::COLUMNINFO CRegFolder::c_rgColumnInfo[] =
{
    { PKEY_ItemNameDisplay, SHCOLSTATE_ONBYDEFAULT, &CRegFolder::_GetDisplayName },
    { PKEY_ItemPathDisplay, SHCOLSTATE_SLOW | SHCOLSTATE_HIDDEN, &CRegFolder::_GetAbsoluteDisplayName },
    { PKEY_ItemFolderPathDisplay, SHCOLSTATE_SLOW | SHCOLSTATE_HIDDEN, &CRegFolder::_GetItemFolderPathDisplay },
    { PKEY_ItemFolderPathDisplayNarrow, SHCOLSTATE_SLOW | SHCOLSTATE_HIDDEN, &CRegFolder::_GetItemFolderPathDisplayNarrow },
    { PKEY_ParsingName, SHCOLSTATE_HIDDEN, &CRegFolder::_GetParsingName },
    { PKEY_RegistryType, SHCOLSTATE_ONBYDEFAULT, &CRegFolder::_GetRegistryType },    
    { PKEY_RegistryData, SHCOLSTATE_SLOW | SHCOLSTATE_HIDDEN, &CRegFolder::_GetRegistryData },
    { PKEY_RegistryDataDisplay, SHCOLSTATE_ONBYDEFAULT | SHCOLSTATE_SLOW, &CRegFolder::_GetRegistryDataDisplay },
    { PKEY_RegistryTypeDisplay, SHCOLSTATE_HIDDEN, &CRegFolder::_GetRegistryTypeDisplay },
    { PKEY_DateModified, SHCOLSTATE_SLOW, &CRegFolder::_GetDateModified },
    // each of these PKEYs should provide a different value
    { PKEY_PropList_TileInfo, SHCOLSTATE_HIDDEN, &CRegFolder::_GetUIPropList },
    { PKEY_PropList_ExtendedTileInfo, SHCOLSTATE_HIDDEN, &CRegFolder::_GetUIPropList },
    { PKEY_PropList_PreviewDetails, SHCOLSTATE_HIDDEN, &CRegFolder::_GetUIPropList },
    { PKEY_PropList_InfoTip, SHCOLSTATE_HIDDEN, &CRegFolder::_GetUIPropList },
    { PKEY_FileAttributes, SHCOLSTATE_HIDDEN, &CRegFolder::_GetFileAttributes },
};

// inits a new instance of CRegFolder with all of the state needed to know where this item is

HRESULT CRegFolder::_Initialize(PCIDLIST_ABSOLUTE pidl, PCUITEMID_CHILD pidlChild, HKEY hkey, PCWSTR pszPath)
{
    _hkey = hkey;

    HRESULT hr = *pszPath ? SHStrDup(pszPath, &_spszPath) : S_OK;
    if (SUCCEEDED(hr))
    {
        hr = CombineIDLists(pidl, pidlChild, &_spidl);
    }
    return hr;
}

// create a new instance of a CRegFolder for a given child ID
HRESULT CRegFolder::_CreateFolder(PCUITEMID_CHILD pidlChild, REFIID riid, __deref_out void **ppv)
{
    *ppv = NULL;
    CComObjectWithRef<CRegFolder> *psprf;
    HRESULT hr = CComObjectWithRef<CRegFolder>::CreateInstance(&psprf);
    if (SUCCEEDED(hr))
    {
        HKEY hkey;
        CRegistryPathString strKey;
        hr = _GetKeyPathForItem(pidlChild, &hkey, &strKey);
        if (SUCCEEDED(hr))
        {
            hr = psprf->_Initialize(_spidl, pidlChild, hkey, strKey);
            if (SUCCEEDED(hr))
            {
                hr = psprf->QueryInterface(riid, ppv);
            }
        }
        psprf->Release();
    }
    return hr;
}

// IPersist
STDMETHODIMP CRegFolder::GetClassID(__out CLSID *pclsid)
{
    *pclsid = CLSID_RegFolder;
    return S_OK;
}

// IPersistFolder
STDMETHODIMP CRegFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
{
    _spidl.Free();
    return CloneFullIDList(pidl, &_spidl);
}

// IPersistFolder2
STDMETHODIMP CRegFolder::GetCurFolder(__out PIDLIST_ABSOLUTE *ppidl)
{
    return CloneFullIDList(_spidl, ppidl);
}

// IShellFolder
// This data source has registered for the "reg:" protocol (see the registry at HKCR\Reg)
// and thus our parsing strings may be prefixed with "reg://" or not.

DECLARE_SZ(RegProtocolPrefix, "reg:");
DECLARE_SZ(RegRootPrefix, "//");
DECLARE_SZ(RegValueExt, ".rv");
DECLARE_SZ(RegKeyExt, ".rk");

HRESULT CRegFolder::_NextNameSegment(__inout PWSTR *ppszInOut, __deref_out PWSTR *ppszSegment, __out REGITEM *pri, __out DWORD *pdwType)
{
    PWSTR pszSegmentStart = *ppszInOut;
    UINT cchInOut = lstrlen(pszSegmentStart);
    PWSTR pszSegmentEnd = pszSegmentStart;
    HRESULT hr = S_OK;

    ZeroMemory(pri, sizeof(REGITEM));

    // Parse "reg:" formats
    if (IsRoot())
    {
        // For the root we accept "reg:", "reg://" and "reg://{sid}" followed by the parsing names
        // Parse "reg:" formats
        if (lstrlen(pszSegmentEnd) >= c_cchRegProtocolPrefix && CompareStringOrdinal(pszSegmentEnd, c_cchRegProtocolPrefix, c_szRegProtocolPrefix, c_cchRegProtocolPrefix, TRUE) == CSTR_EQUAL)
        {
            // Skip "reg:"
            pszSegmentEnd += c_cchRegProtocolPrefix;

            // Check for the "//" prefix
            if (lstrlen(pszSegmentEnd) >= c_cchRegRootPrefix && CompareStringOrdinal(pszSegmentEnd, c_cchRegRootPrefix, c_szRegRootPrefix, c_cchRegRootPrefix, TRUE) == CSTR_EQUAL)
            {
                // Skip "//"
                pszSegmentEnd += c_cchRegRootPrefix;

                // Check for the "{SID}" host component
                if (*pszSegmentEnd == L'{')
                {
                    // Skip "{SID}"
                    pszSegmentEnd = StrChr(pszSegmentEnd, L'}');
                    if (pszSegmentEnd)
                    {
                        pszSegmentEnd++;
                    }
                    else
                    {
                        // Malformed {SID}
                        hr = E_INVALIDARG;
                    }
                }
            }

            if (SUCCEEDED(hr))
            {
                // This should be considered invalid input (E_INVALIDARG), but to support the way 
                // the scope picker/indexer works we have to force the parse of "reg://{SID}" to work.

                pri->rit = RIT_NAMESPACEROOT;
                pri->hkey = NULL;
                *pdwType = REG_NONE;
            }
        }
    }
    
    if (SUCCEEDED(hr) && pri->rit != RIT_NAMESPACEROOT)
    {
        // Parse registry path formats
        while (*pszSegmentEnd && (*pszSegmentEnd != L'\\' && *pszSegmentEnd != L'/'))
        {
            pszSegmentEnd++;
        }
    }

    // Allocate and return the segment
    if (SUCCEEDED(hr))
    {
        CComMemPtr<WCHAR> spszSegment;
        hr = CoAllocStringLen(pszSegmentStart, pszSegmentEnd - pszSegmentStart, &spszSegment);
        if (SUCCEEDED(hr))
        {
            if (pri->rit != RIT_NAMESPACEROOT)
            {
                REGITEMTYPE priInferred;
                CComMemPtr<WCHAR> spszRegName;
                hr = _RegNameFromParsingName(spszSegment, &priInferred, &spszRegName);
                if (SUCCEEDED(hr))
                {
                    spszSegment.Free();
                    spszSegment.Attach(spszRegName.Detach());
                    if (IsRoot())
                    {
                        pri->rit = RIT_ROOTKEY;
                    }
                    else
                    {
                        pri->rit = priInferred;
                    }
                    pri->hkey = _hkey;
                    if (pri->rit == RIT_VALUE)
                    {
                        if (*spszSegment)
                        {
                            CRegKey rk;
                            hr = _OpenKeyForFolder(KEY_QUERY_VALUE, &rk);
                            if (SUCCEEDED(hr))
                            {
                                hr = AtlHresultFromWin32(rk.QueryValue(spszSegment, pdwType, NULL, 0));
                            }
                        }
                        else
                        {
                            // default value
                            *pdwType = REG_SZ;
                        }
                    }
                    else
                    {
                        *pdwType = REG_KEY;
                    }
                }
            }

            // Skip trailing delimeter
            if (*pszSegmentEnd == L'\\' || *pszSegmentEnd == L'/')
            {
                pszSegmentEnd++;
            }

            *ppszSegment = spszSegment.Detach();
            *ppszInOut = pszSegmentEnd;
        }
    }
    return hr;
}

static const struct
{
    HKEY hkey;
    PCWSTR pszName;
}
c_rgMapHKeyToName[] = 
{
    { HKEY_CLASSES_ROOT,    L"HKEY_CLASSES_ROOT" },
    { HKEY_CURRENT_USER,    L"HKEY_CURRENT_USER" },
    { HKEY_LOCAL_MACHINE,   L"HKEY_LOCAL_MACHINE" },
    { HKEY_USERS,           L"HKEY_USERS" },
    { HKEY_CURRENT_CONFIG,  L"HKEY_CURRENT_CONFIG" },
};

HRESULT CRegFolder::_MapStringToHKEY(PCWSTR pszKey, __out HKEY *phkey)
{
    HRESULT hr = E_FAIL;
    for (int i = 0; i < ARRAYSIZE(c_rgMapHKeyToName) && FAILED(hr); i++)
    {
        if (CompareStringOrdinal(pszKey, -1, c_rgMapHKeyToName[i].pszName, -1, TRUE) == CSTR_EQUAL)
        {
            *phkey = c_rgMapHKeyToName[i].hkey;
            hr = S_OK;
        }
    }
    return hr;
}

HRESULT CRegFolder::_CreateItemID(REGITEM *pri, PCWSTR pszRegName, DWORD dwType, __deref_out PITEMID_CHILD *ppidl)
{
    CComPtr<IPropertyStore> sppropstore;
    HRESULT hr = PSCreateMemoryPropertyStore(IID_PPV_ARGS(&sppropstore));
    if (SUCCEEDED(hr))
    {
        CComMemPtr<WCHAR> spszParsingName;
        hr = _ParsingNameFromRegName(pszRegName, pri->rit, &spszParsingName);
        if (SUCCEEDED(hr))
        {
            CComPropVariant spropvar;
            hr = InitPropVariantFromString(spszParsingName, &spropvar);
            if (SUCCEEDED(hr))
            {
                hr = sppropstore->SetValue(PKEY_ParsingName, spropvar);
                if (SUCCEEDED(hr))
                {
                    spropvar = dwType;
                    hr = sppropstore->SetValue(PKEY_RegistryType, spropvar);

                    if (SUCCEEDED(hr) && pri->rit == RIT_ROOTKEY)
                    {
                        // if hkey is NULL (for the root case) pszParsingName must be one of the known registry key names
                        hr = _MapStringToHKEY(pszRegName, &pri->hkey);
                    }

                    if (SUCCEEDED(hr))
                    {
                        hr = CreateItemID(pri, sppropstore, ppidl);
                    }
                }
            }
        }
    }
    return hr;
}

HRESULT CRegFolder::_IsValue(PCUIDLIST_RELATIVE pidl, __out BOOL *pfIsValue)
{
    *pfIsValue = FALSE;

    PCREGITEM pri;
    HRESULT hr = GetDataFromIDList(pidl, &pri);
    if (SUCCEEDED(hr))
    {
        *pfIsValue = pri->rit == RIT_VALUE;
    }
    return hr;
}

HRESULT CRegFolder::_GetParsingName(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{
    return GetPropertyFromIDList(pidl, PKEY_ParsingName, ppropvar);
}

HRESULT CRegFolder::_ParsingNameFromRegName(PCWSTR pszRegName, REGITEMTYPE rit, __deref_out PWSTR *ppszParsingName)
{
    HRESULT hr = S_OK;

    CRegistryPathString strParsingPath;
    if (rit != RIT_NAMESPACEROOT && rit != RIT_ROOTKEY)
    {
        while (*pszRegName && SUCCEEDED(hr))
        {
            WCHAR ch = *pszRegName;
            switch (ch)
            {
            case L'\\':
            case L'/':
            case L'|':
            case L'%':
            case L'?':
            case L'*':
                {
                    CBufString strEscaped;
                    hr = strEscaped.Format(L"%%%.2x", ch);
                    if (SUCCEEDED(hr))
                    {
                        hr = strParsingPath.Append(strEscaped);
                    }
                }
                break;

            default:
                hr = strParsingPath.Append(ch);
                break;
            }
            pszRegName++;
        }
    }
    else
    {
        hr = strParsingPath.Copy(pszRegName);
    }

    if (SUCCEEDED(hr))
    {
        // add the extension
        switch (rit)
        {
        case RIT_VALUE:
            hr = strParsingPath.Append(c_szRegValueExt);
            break;

        case RIT_NAMESPACEROOT:
            break;

        default:
            hr = strParsingPath.Append(c_szRegKeyExt);
            break;
        }

        if (SUCCEEDED(hr))
        {
            hr = strParsingPath.CoTaskMemCopy(ppszParsingName);
        }
    }

    return hr;
}

HRESULT CRegFolder::_RegNameFromParsingName(PCWSTR pszParsingName, __out_opt REGITEMTYPE *prit, __deref_out PWSTR *ppszRegName)
{
    HRESULT hr = S_OK;

    size_t cchParsing = lstrlen(pszParsingName);

    // we could make a pass and figure out how large the output string will be exactly, but 
    // it most cases it will be exactly the same, and is guaranteed to be less than or equal in length
    CRegistryPathString strParsingPath;
    PCWSTR pszCurrent = pszParsingName;
    while (*pszCurrent && SUCCEEDED(hr))
    {
        WCHAR ch = *pszCurrent;
        if (ch == L'%')
        {
            if (*++pszCurrent)
            {
                WCHAR ch1 = *pszCurrent;
                if (*++pszCurrent)
                {
                    WCHAR ch2 = *pszCurrent;
                    WCHAR szEscapedSegment[] = { L'0', L'x', ch1, ch2, L'\0'};
                    int chEscaped;
                    // this is a bit inaccurate because this will allow input like 0x4q (interpreted as 4)
                    hr = StrToIntEx(szEscapedSegment, STIF_SUPPORT_HEX, &chEscaped) ? S_OK : E_INVALIDARG;
                    if (SUCCEEDED(hr))
                    {
                        strParsingPath.Append((WCHAR)chEscaped);
                    }
                }
            }

        }
        else
        {
            hr = strParsingPath.Append(pszCurrent, 1);
        }
        pszCurrent++;
    }

    if (SUCCEEDED(hr))
    {
        REGITEMTYPE rit = RIT_SUBKEY;
        // remove the extension if present
        PWSTR pszExtension = PathFindExtension(strParsingPath);
        if (pszExtension)
        {
            if (CompareStringOrdinal(pszExtension, -1, c_szRegValueExt, c_cchRegValueExt, TRUE) == CSTR_EQUAL)
            {
                rit = RIT_VALUE;
                *pszExtension = L'\0';
            }
            else if (CompareStringOrdinal(pszExtension, -1, c_szRegKeyExt, c_cchRegKeyExt, TRUE) == CSTR_EQUAL)
            {
                *pszExtension = L'\0';
            }
        }

        if (prit)
        {
            *prit = rit;
        }

        hr = strParsingPath.CoTaskMemCopy(ppszRegName);
    }

    return hr;
}

HRESULT CRegFolder::_GetAbsoluteDisplayName(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{
    CComMemPtr<WCHAR> spszName;
    HRESULT hr = _GetAbsoluteFriendlyName(pidl, &spszName);
    if (SUCCEEDED(hr))
    {
        hr = InitPropVariantFromString(spszName, ppropvar);
    }
    return hr;
}

HRESULT CRegFolder::_GetItemFolderPathDisplay(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{
    CComMemPtr<WCHAR> spszName;
    HRESULT hr = SHGetNameFromIDList(_spidl, SIGDN_DESKTOPABSOLUTEEDITING, &spszName);
    if (SUCCEEDED(hr))
    {
        hr = InitPropVariantFromString(spszName, ppropvar);
    }
    return hr;
}

HRESULT CRegFolder::_GetItemFolderPathDisplayNarrow(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{
    CComMemPtr<WCHAR> spszName;
    HRESULT hr = SHGetNameFromIDList(_spidl, SIGDN_DESKTOPABSOLUTEEDITING, &spszName);
    if (SUCCEEDED(hr))
    {
        CComMemPtr<WCHAR> spszNameNarrow;
        hr = PathGetNarrowPath(spszName, &spszNameNarrow);
        if (SUCCEEDED(hr))
        {
            hr = InitPropVariantFromString(spszNameNarrow, ppropvar);
        }
    }
    return hr;
}

HRESULT CRegFolder::_GetDisplayName(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{
    PCREGITEM pri;
    HRESULT hr = GetDataFromIDList(pidl, &pri);
    if (SUCCEEDED(hr))
    {
        CComPropVariant spropvar;
        hr = GetPropertyFromIDList(pidl, PKEY_ParsingName, &spropvar);
        if (SUCCEEDED(hr))
        {
            if (pri->rit == RIT_NAMESPACEROOT)
            {
                // This the root of the namespace
                spropvar.Clear();

                CBufString str;
                hr = str.Load(_AtlBaseModule.GetModuleInstance(), IDS_ROOTFOLDERDISPLAYNAME);
                if (SUCCEEDED(hr))
                {
                    hr = InitPropVariantFromString(str, &spropvar);
                }
            }
            else if (pri->rit == RIT_VALUE && CompareStringOrdinal(spropvar.pwszVal, -1, c_szRegValueExt, c_cchRegValueExt, TRUE) == CSTR_EQUAL)
            {
                // This is the default value, load a friendly name for it
                spropvar.Clear();

                CBufString str;
                hr = str.Load(IDS_DEFAULTVALUE);
                if (SUCCEEDED(hr))
                {
                    hr = InitPropVariantFromString(str, &spropvar);
                }
            }
            else
            {
                CRegistryPathString strParsingName;
                hr = PropVariantToString(spropvar, strParsingName, strParsingName.Size());
                if (SUCCEEDED(hr))
                {
                    CComMemPtr<WCHAR> spszRegName;
                    hr = _RegNameFromParsingName(strParsingName, NULL, &spszRegName);
                    if (SUCCEEDED(hr))
                    {
                        spropvar.Clear();
                        hr = InitPropVariantFromString(spszRegName, &spropvar);
                    }
                }
            }

            if (SUCCEEDED(hr))
            {
                hr = spropvar.Detach(ppropvar);
            }
        }
    }
    return hr;
}

HRESULT CRegFolder::_GetRegistryType(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{
    return GetPropertyFromIDList(pidl, PKEY_RegistryType, ppropvar);
}

HRESULT CRegFolder::_GetRegistryTypeDisplay(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{    
    CComPropVariant spropvar;
    HRESULT hr = _GetRegistryType(pidl, &spropvar);
    if (SUCCEEDED(hr))
    {        
        CComPtr<IPropertyDescription> sppropdesc;
        hr = PSGetPropertyDescription(PKEY_RegistryType, IID_PPV_ARGS(&sppropdesc));
        if (SUCCEEDED(hr))
        {
            CComMemPtr<WCHAR> spszDisplayName;
            hr = sppropdesc->FormatForDisplay(spropvar, PDFF_DEFAULT, &spszDisplayName);
            if (SUCCEEDED(hr))
            {                    
                hr = InitPropVariantFromString(spszDisplayName, ppropvar);                    
            }
        }                 
    }
    return hr;
}

HRESULT CRegFolder::_OpenKeyForFolder(DWORD dwAccess, __out CRegKey *prk)
{
    prk->m_hKey = NULL;
    return AtlHresultFromWin32(prk->Open(_hkey, _spszPath, dwAccess));
}

HRESULT CRegFolder::_GetRegistryData(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{
    BOOL fIsValue;
    HRESULT hr = _IsValue(pidl, &fIsValue);
    if (SUCCEEDED(hr))
    {
        hr = E_FAIL;
        if (fIsValue)
        {
            CRegKey rk;
            hr = _OpenKeyForFolder(KEY_QUERY_VALUE, &rk);
            if (SUCCEEDED(hr))
            {
                CComMemPtr<WCHAR> spszName;
                hr = _GetParsingName(pidl, &spszName);
                if (SUCCEEDED(hr))
                {
                    CComMemPtr<WCHAR> spszRegName;
                    hr = _RegNameFromParsingName(spszName, NULL, &spszRegName);
                    if (SUCCEEDED(hr))
                    {
                        DWORD dwType;
                        ULONG cb;
                        hr = AtlHresultFromWin32(rk.QueryValue(spszRegName, &dwType, NULL, &cb));
                        if (SUCCEEDED(hr))
                        {
                            CComPropVariant spropvar;
                            switch (dwType)
                            {
                            case REG_SZ:
                            case REG_EXPAND_SZ:
                                {
                                    CComMemPtr<WCHAR> spszVal;
                                    hr = spszVal.Allocate(cb / sizeof(WCHAR) + 1);
                                    if (SUCCEEDED(hr))
                                    {
                                        hr = AtlHresultFromWin32(rk.QueryValue(spszRegName, &dwType, spszVal, &cb));
                                        if (SUCCEEDED(hr))
                                        {
                                            spropvar.pwszVal = spszVal.Detach();
                                            spropvar.vt = VT_LPWSTR;
                                        }
                                    }
                                }
                                break;

                            case REG_BINARY:
                            case REG_MULTI_SZ:
                                {
                                    CComMemPtr<BYTE> spblob;
                                    hr = spblob.Allocate(cb);
                                    if (SUCCEEDED(hr))
                                    {
                                        hr = AtlHresultFromWin32(rk.QueryValue(spszRegName, &dwType, spblob, &cb));
                                        if (SUCCEEDED(hr))
                                        {
                                            spropvar.blob.pBlobData = spblob.Detach();
                                            spropvar.blob.cbSize = cb;
                                            spropvar.vt = VT_BLOB;
                                        }
                                    }
                                }
                                break;

                            case REG_DWORD:
                                hr = AtlHresultFromWin32(rk.QueryDWORDValue(spszRegName, spropvar.ulVal));
                                if (SUCCEEDED(hr))
                                {
                                    spropvar.vt = VT_UI4;
                                }
                                break;

                            case REG_QWORD:
                                hr = AtlHresultFromWin32(rk.QueryQWORDValue(spszRegName, spropvar.uhVal.QuadPart));
                                if (SUCCEEDED(hr))
                                {
                                    spropvar.vt = VT_UI8;
                                }
                                break;

                            default:
                                hr = E_FAIL;
                            }

                            if (SUCCEEDED(hr))
                            {
                                hr = spropvar.Detach(ppropvar);
                            }
                        }
                        else if ((!spszRegName || !spszRegName[0]) && hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
                        {
                            // We put in a placeholder for the default value if it doesn't exist.  Make sure we
                            // can return a value for it.
                            CComMemPtr<WCHAR> spszVal;
                            hr = SHStrDup(L"", &spszVal);
                            if (SUCCEEDED(hr))
                            {
                                ppropvar->vt = VT_LPWSTR;
                                ppropvar->pwszVal = spszVal.Detach();
                            }
                        }
                    }
                }
            }
        }
    }
    return hr;
}

HRESULT CRegFolder::_BinaryToText(const BYTE *pblob, DWORD cb, __out PWSTR *ppszText)
{
    const WCHAR c_wcDigits[] =
    {
        L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
        L'8', L'9', L'a', L'b', L'c', L'd', L'e', L'f',
    };

    CComMemPtr<WCHAR> spszText;
    HRESULT hr = spszText.Allocate((cb * 2) + (cb - 1) + 1);  // 2 chars/byte + 1 char spacer between bytes
    if (SUCCEEDED(hr))
    {
        BYTE *pb = const_cast<BYTE *>(pblob);
        PWSTR pszTemp;
        for (pszTemp = spszText; cb != 0; cb--)
        {
            BYTE b = *pb++;
            *pszTemp++ = c_wcDigits[(b >> 4) & 0x000f];
            *pszTemp++ = c_wcDigits[b & 0x000f];
            if (cb > 1)
            {
                *pszTemp++ = L' ';
            }
        }
        *pszTemp = '\0';
        *ppszText = spszText.Detach();
    }
    return hr;
}

HRESULT CRegFolder::_GetRegistryDataDisplay(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{
    CComPropVariant spropvar;
    HRESULT hr = _GetRegistryData(pidl, &spropvar);
    if (SUCCEEDED(hr))
    {
        switch (spropvar.vt)
        {
        case VT_LPWSTR:
            // Already formatted
            hr = spropvar.Detach(ppropvar);
            break;

        case VT_UI4:
            {
                CBufString str;
                hr = StringCchPrintf(str, str.Size(), L"0x%0.8x (%u)", spropvar.ulVal, spropvar.ulVal);
                if (SUCCEEDED(hr))
                {
                    CComPropVariant spropvarOut;
                    hr = InitPropVariantFromString(str, &spropvarOut);
                    if (SUCCEEDED(hr))
                    {
                        hr = spropvarOut.Detach(ppropvar);
                    }
                }
            }
            break;

        case VT_UI8:
            {
                CBufString str;
                hr = StringCchPrintf(str, str.Size(), L"0x%0.16llx (%llu)", spropvar.uhVal.QuadPart, spropvar.uhVal.QuadPart);
                if (SUCCEEDED(hr))
                {
                    CComPropVariant spropvarOut;
                    hr = InitPropVariantFromString(str, &spropvarOut);
                    if (SUCCEEDED(hr))
                    {
                        hr = spropvarOut.Detach(ppropvar);
                    }
                }
            }
            break;

        case VT_BLOB:
            {
                CComPropVariant spropvarType;
                hr = _GetRegistryType(pidl, &spropvarType);
                if (SUCCEEDED(hr))
                {
                    switch (spropvarType.ulVal)
                    {
                    case REG_MULTI_SZ:
                        {
                            // Replace inline NULLs with a better delimiter
                            CComMemPtr<WCHAR> spszVal;
                            DWORD cch = spropvar.blob.cbSize / sizeof(WCHAR);
                            hr = spszVal.Allocate(cch);
                            if (SUCCEEDED(hr))
                            {
                                CopyMemory(spszVal, spropvar.blob.pBlobData, spropvar.blob.cbSize);
                                PWSTR psz = spszVal;
                                while (psz < spszVal + cch - 2)
                                {
                                    if (!*psz)
                                    {
                                        *psz = 0x2551;
                                    }
                                    psz++;
                                }
                                ppropvar->pwszVal = spszVal.Detach();
                                ppropvar->vt = VT_LPWSTR;
                            }
                        }
                        break;

                    default:
                        {
                            CComMemPtr<WCHAR> spszBlob;
                            hr = _BinaryToText(spropvar.blob.pBlobData, spropvar.blob.cbSize, &spszBlob);
                            if (SUCCEEDED(hr))
                            {
                                ppropvar->pwszVal = spszBlob.Detach();
                                ppropvar->vt = VT_LPWSTR;
                            }
                        }
                        break;

                    }
                }
            }
        }
    }
    return hr;
}

// return the KEY and path pair
HRESULT CRegFolder::_GetKeyPathForItem(__in PCUITEMID_CHILD pidl, __out HKEY *phkey, __inout CRegistryPathString *pstrKey)
{
    pstrKey->Empty();

    PCREGITEM pri;
    HRESULT hr = GetDataFromIDList(pidl, &pri);
    if (SUCCEEDED(hr))
    {
        *phkey = pri->hkey;

        if (pri->rit == RIT_VALUE)
        {
            if (_spszPath)
            {
                hr = pstrKey->Copy(_spszPath);
            }
            else
            {
                hr = E_UNEXPECTED;
            }
        }
        else if (pri->rit == RIT_SUBKEY)
        {
            CComMemPtr<WCHAR> spszName;
            hr = _GetParsingName(pidl, &spszName);
            if (SUCCEEDED(hr))
            {
                CComMemPtr<WCHAR> spszRegName;
                hr = _RegNameFromParsingName(spszName, NULL, &spszRegName);
                if (SUCCEEDED(hr))
                {
                    if (_spszPath)
                    {
                        hr = pstrKey->Copy(_spszPath);
                        if (SUCCEEDED(hr))
                        {
                            hr = pstrKey->Append(L'\\');
                            if (SUCCEEDED(hr))
                            {
                                hr = pstrKey->Append(spszRegName);
                            }
                        }
                    }
                    else
                    {
                        hr = pstrKey->Copy(spszRegName);
                    }
                }
            }
        }
        else if (pri->rit != RIT_ROOTKEY && pri->rit != RIT_NAMESPACEROOT)
        {
            // RIT_VALUE or bad data in the IDList
            hr = E_FAIL;
        }
    }
    return hr;
}

HRESULT CRegFolder::_OpenKeyForItem(PCUITEMID_CHILD pidl, DWORD dwAccess, CRegKey *prk)
{
    prk->m_hKey = NULL;

    HKEY hkey;
    CRegistryPathString strKey;
    HRESULT hr = _GetKeyPathForItem(pidl, &hkey, &strKey);
    if (SUCCEEDED(hr) && hkey)
    {
        hr = AtlHresultFromWin32(prk->Open(hkey, strKey, dwAccess));
    }
    return hr;
}

HRESULT CRegFolder::_GetFileAttributes(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{
    PCREGITEM pri;
    HRESULT hr = GetDataFromIDList(pidl, &pri);
    if (SUCCEEDED(hr))
    {
        DWORD dwFileAttribs = pri->rit == RIT_VALUE
            ? FILE_ATTRIBUTE_NORMAL 
            : FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY;

        CRegKey rk;
        if (FAILED(_OpenKeyForItem(pidl, KEY_QUERY_VALUE, &rk)))
        {
            // this should be failing in the case of (default) reg values which we don't want to index
            dwFileAttribs |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
        }
        hr = InitPropVariantFromUInt32(dwFileAttribs, ppropvar);
    }
    return hr;
}

HRESULT CRegFolder::_GetDateModified(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{
    CRegKey rk;
    HRESULT hr = _OpenKeyForItem(pidl, KEY_QUERY_VALUE, &rk);
    if (SUCCEEDED(hr))
    {
        FILETIME ftLastModified;
        hr = AtlHresultFromWin32(RegQueryInfoKey(rk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &ftLastModified));
        if (SUCCEEDED(hr))
        {
            hr = InitPropVariantFromFileTime(&ftLastModified, ppropvar);
        }
    }
    return hr;
}

HRESULT CRegFolder::_GetUIPropList(PCUITEMID_CHILD pidl, __out PROPVARIANT *ppropvar)
{
    return InitPropVariantFromString(L"prop:Windows.Registry.Type;Windows.Registry.DataDisplay;System.DateModified;", ppropvar);
}

HRESULT CRegFolder::_GetPropertyForItem(PCUITEMID_CHILD pidl, REFPROPERTYKEY key, __out PROPVARIANT *ppropvar)
{
    HRESULT hr = E_FAIL;
    for (int i = 0; i < ARRAYSIZE(c_rgColumnInfo); i++)
    {
        if (IsEqualPropertyKey(key, c_rgColumnInfo[i].key))
        {
            hr = (this->*c_rgColumnInfo[i].pfnGetProperty)(pidl, ppropvar);
            break;
        }
    }
    return hr;
}

HRESULT CRegFolder::_ValidateParsedIDList(PCUITEMID_CHILD pidl)
{
    CRegKey rk;
    return _OpenKeyForItem(pidl, KEY_READ, &rk);
}

STDMETHODIMP CRegFolder::ParseDisplayName(HWND hwnd, __in IBindCtx *pbc, __in PWSTR pszDisplayName, __inout ULONG *pchEaten, __deref_out PIDLIST_RELATIVE *ppidl, __inout ULONG *pdwAttributes)
{
    REGITEM ri;
    DWORD dwType;
    CLocalMemPtr<WCHAR> spszSegment;
    HRESULT hr = _NextNameSegment(&pszDisplayName, &spszSegment, &ri, &dwType);
    if (SUCCEEDED(hr))
    {
        // Determine what sort of item to create based on the name
        CComMemPtr<ITEMID_CHILD> spidlSegment;
        hr = _CreateItemID(&ri, spszSegment, dwType, &spidlSegment);
        if (SUCCEEDED(hr))
        {
            hr = _ValidateParsedIDList(spidlSegment);
            if (SUCCEEDED(hr))
            {
                if (*pszDisplayName)
                {
                    // More to parse
                    CComPtr<IShellFolder> spsf;
                    hr = BindToObject(spidlSegment, pbc, IID_PPV_ARGS(&spsf));
                    if (SUCCEEDED(hr))
                    {
                        CComMemPtr<ITEMIDLIST_RELATIVE> spidlNext;
                        hr = spsf->ParseDisplayName(hwnd, pbc, const_cast<WCHAR *>(pszDisplayName), NULL, &spidlNext, pdwAttributes);
                        if (SUCCEEDED(hr))
                        {
                            hr = CombineIDLists(reinterpret_cast<PCIDLIST_ABSOLUTE>((PIDLIST_RELATIVE)spidlSegment), reinterpret_cast<PCIDLIST_ABSOLUTE>((PIDLIST_RELATIVE)spidlNext), reinterpret_cast<PIDLIST_ABSOLUTE *>(ppidl));
                        }
                    }
                }
                else
                {
                    *ppidl = spidlSegment.Detach();
                    if (pdwAttributes && *pdwAttributes)
                    {
                        PCUITEMID_CHILD pidlFirst = reinterpret_cast<PCUITEMID_CHILD>(*ppidl);
                        hr = GetAttributesOf(1, &pidlFirst, pdwAttributes);
                    }
                }
            }
        }
    }
    return hr;
}

HRESULT CRegFolder::_CreateChildIDListArray(SHCONTF grfFlags, __inout CAtlArray<PITEMID_CHILD> &rgItems)
{
    HRESULT hr = E_FAIL;
    try
    {
        if (_hkey)
        {
            // sub key case

            if (grfFlags & SHCONTF_FOLDERS)
            {
                // _spszPath may be NULL here

                CRegKey rk;
                hr = _OpenKeyForFolder(KEY_ENUMERATE_SUB_KEYS, &rk);
                if (SUCCEEDED(hr))
                {
                    CRegistryPathString strKey;
                    DWORD cchKey;
                    for (DWORD dwIndex = 0; SUCCEEDED(hr); dwIndex++)
                    {
                        cchKey = strKey.Size();
                        LONG lRet = rk.EnumKey(dwIndex, strKey, &cchKey);
                        if (lRet == ERROR_SUCCESS)
                        {
                            CComMemPtr<ITEMID_CHILD> spidl;
                            REGITEM ri = { IsRoot() ? RIT_ROOTKEY : RIT_SUBKEY, _hkey };
                            hr = _CreateItemID(&ri, strKey, REG_KEY, &spidl);
                            if (SUCCEEDED(hr))
                            {
                                // look at this (and other instances of this)
                                rgItems.Add(spidl);
                                spidl.Detach();
                            }
                        }
                        else if (lRet != ERROR_NO_MORE_ITEMS)
                        {
                            hr = AtlHresultFromWin32(lRet);
                        }
                        else
                        {
                            hr = S_OK;
                            break;
                        }
                    }
                }
            }

            if (grfFlags & SHCONTF_NONFOLDERS)
            {
                CRegKey rk;
                hr = _OpenKeyForFolder(KEY_QUERY_VALUE, &rk);
                if (SUCCEEDED(hr))
                {
                    // Always add the default value first.  Then, skip it during the enumeration below
                    CComMemPtr<ITEMID_CHILD> spidlDefault;
                    REGITEM ri = { RIT_VALUE, _hkey };
                    hr = _CreateItemID(&ri, L"", REG_SZ, &spidlDefault);
                    if (SUCCEEDED(hr))
                    {
                        rgItems.Add(spidlDefault);
                        spidlDefault.Detach();

                        CRegistryPathString strValue;
                        DWORD cchValue;
                        for (DWORD dwIndex = 0; SUCCEEDED(hr); dwIndex++)
                        {
                            cchValue = strValue.Size();
                            DWORD dwType;
                            LONG lRet = RegEnumValue(rk, dwIndex, strValue, &cchValue, NULL, &dwType, NULL, NULL);
                            if (lRet == ERROR_SUCCESS && *((PCWSTR)(strValue)))
                            {
                                CComMemPtr<ITEMID_CHILD> spidl;
                                ri.rit = RIT_VALUE;
                                hr = _CreateItemID(&ri, strValue, dwType, &spidl);
                                if (SUCCEEDED(hr))
                                {
                                    rgItems.Add(spidl);
                                    spidl.Detach();
                                }
                            }
                            else if (lRet != ERROR_NO_MORE_ITEMS)
                            {
                                hr = AtlHresultFromWin32(lRet);
                            }
                            else
                            {
                                hr = S_OK;
                                break;
                            }
                        }
                    }
                }
            }
        }
        else if (grfFlags & SHCONTF_FOLDERS)
        {
            // Must be the root
            hr = S_OK;
            for (size_t i = 0; i < ARRAYSIZE(c_rgMapHKeyToName) && SUCCEEDED(hr); i++)
            {
                CComMemPtr<ITEMID_CHILD> spidl;
                REGITEM ri = { IsRoot() ? RIT_ROOTKEY : RIT_SUBKEY, c_rgMapHKeyToName[i].hkey };
                hr = _CreateItemID(&ri, c_rgMapHKeyToName[i].pszName, REG_KEY, &spidl);
                if (SUCCEEDED(hr))
                {
                    rgItems.Add(spidl);
                    spidl.Detach();
                }
            }
        }
    }
    catch (CAtlException &e)
    {
        hr = e.m_hr;
    }
    return hr;
}

class CChildItemIDPolicy
{
public:
    static void init(PITEMID_CHILD *ppidl)
    {
    }
    static void destroy(PITEMID_CHILD *ppidl)
    {
        ILFree(*ppidl);
    }
    static HRESULT copy(PITEMID_CHILD *ppidlDst, PITEMID_CHILD *ppidlSrc)
    {
        return CloneChildIDList(*ppidlSrc, ppidlDst);
    }
};

STDMETHODIMP CRegFolder::EnumObjects(HWND hwnd, SHCONTF grfFlags, __deref_out IEnumIDList **ppenmIDList)
{
    typedef CComEnum<IEnumIDList, &__uuidof(IEnumIDList), PITEMID_CHILD, CChildItemIDPolicy> CEnumIDList;

    CAtlArray<PITEMID_CHILD> rgItems;
    HRESULT hr = _CreateChildIDListArray(grfFlags, rgItems);
    if (SUCCEEDED(hr))
    {
        CComObject<CEnumIDList> *pspenm;
        hr = CComObject<CEnumIDList>::CreateInstance(&pspenm);
        if (SUCCEEDED(hr))
        {
            pspenm->AddRef();
            hr = pspenm->Init(rgItems.GetData(), rgItems.GetData() + rgItems.GetCount(), NULL, AtlFlagCopy);
            if (SUCCEEDED(hr))
            {
                hr = pspenm->QueryInterface(IID_PPV_ARGS(ppenmIDList));
            }
            pspenm->Release();
        }
    }
    // Free the array
    for (size_t i = 0; i < rgItems.GetCount(); i++)
    {
        ILFree(rgItems.GetAt(i));
    }
    return hr;
}

STDMETHODIMP CRegFolder::BindToObject(PCUIDLIST_RELATIVE pidl, __in IBindCtx *pbc, REFIID riid, __deref_out void **ppv)
{
    *ppv = NULL;
    HRESULT hr = E_NOINTERFACE;
    if (ILIsChild(pidl))
    {
        if (riid == __uuidof(IShellFolder) || riid == __uuidof(IShellFolder2))
        {
            // folder (data source) handler
            hr = _CreateFolder(reinterpret_cast<PCUITEMID_CHILD>(pidl), riid, ppv);
        }
        else if (riid == __uuidof(IPropertyStoreFactory))
        {
            // property store factory handler
            hr = CreateRegistryPropertyStoreFactory(static_cast<IShellFolder *>(this), reinterpret_cast<PCUITEMID_CHILD>(pidl), riid, ppv);
        }
        // add more handlers types here using "else if" blocks
    }
    else
    {
        CComPtr<IShellFolder> ppsf;
        PCUITEMID_CHILD pidlChild;
        hr = SHBindToFolderIDListParent(this, pidl, IID_PPV_ARGS(&ppsf), &pidlChild);
        if (SUCCEEDED(hr))
        {
            hr = ppsf->BindToObject(pidlChild, pbc, riid, ppv);
        }
    }

    return hr;
}

STDMETHODIMP CRegFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, __in IBindCtx *pbc, REFIID riid, __deref_out void **ppv)
{
    return BindToObject(pidl, pbc, riid, ppv);
}

HRESULT CRegFolder::_CompareRelativeIDLists(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
{
    HRESULT hr = S_OK;
    short sResult;
    PCUIDLIST_RELATIVE pidlRel1 = ILNext(pidl1);
    PCUIDLIST_RELATIVE pidlRel2 = ILNext(pidl2);
    if (ILIsEmpty(pidlRel1))
    {
        if (ILIsEmpty(pidlRel2))
        {
            sResult = 0;
        }
        else
        {
            sResult = -1;
        }
    }
    else
    {
        if (ILIsEmpty(pidlRel2))
        {
            sResult = 1;
        }
        else
        {
            // Relative IDLISTs both have data, bind to them and let the subfolder do the comparison
            CComMemPtr<ITEMID_CHILD> spidlNext;
            hr = CloneFirstIDList(pidl1, &spidlNext);
            if (SUCCEEDED(hr))
            {
                CComPtr<IShellFolder2> spsf2;
                hr = BindToObject(spidlNext, NULL, IID_PPV_ARGS(&spsf2));
                if (SUCCEEDED(hr))
                {
                    hr = spsf2->CompareIDs(lParam & ~SHCIDS_COLUMNMASK, pidlRel1, pidlRel2);
                    if (SUCCEEDED(hr))
                    {
                        sResult = (short)HRESULT_CODE(hr);
                    }
                }
            }
        }
    }
    if (SUCCEEDED(hr))
    {
        hr = CompareResultFromInt(sResult);
    }
    return hr;
}

STDMETHODIMP CRegFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
{
    PCREGITEM pri1;
    HRESULT hr = GetDataFromIDList(pidl1, &pri1);
    if (SUCCEEDED(hr))
    {
        PCREGITEM pri2;
        hr = GetDataFromIDList(pidl2, &pri2);
        if (SUCCEEDED(hr))
        {
            short sResult;
            if (pri1->rit != pri2->rit)
            {
                sResult = pri1->rit - pri2->rit;
            }
            else
            {
                CComPropVariant spropvar1;
                hr = GetPropertyFromIDList(pidl1, PKEY_ParsingName, &spropvar1);
                if (SUCCEEDED(hr))
                {
                    CComPropVariant spropvar2;
                    hr = GetPropertyFromIDList(pidl2, PKEY_ParsingName, &spropvar2);
                    if (SUCCEEDED(hr))
                    {
                        sResult = PropVariantCompare(spropvar1, spropvar2);
                    }
                }
            }
            if (SUCCEEDED(hr))
            {
                if (!sResult)
                {
                    // There may be multiple levels in the IDLIST(s).  Compare those also
                    hr = _CompareRelativeIDLists(lParam, pidl1, pidl2);
                }
                else
                {
                    hr = CompareResultFromInt(sResult);
                }
            }
        }
    }
    return hr;
}

STDMETHODIMP CRegFolder::CreateViewObject(HWND hwndOwner, REFIID riid, __deref_out void **ppv)
{
    *ppv = NULL;
    HRESULT hr = E_NOINTERFACE;
    if (riid == __uuidof(IShellView))
    {
        SFV_CREATE sfvc = {sizeof(sfvc)};
        hr = QueryInterface(IID_PPV_ARGS(&sfvc.pshf));
        if (SUCCEEDED(hr))
        {
            hr = CViewCallback::s_CreateInstance(this, IID_PPV_ARGS(&sfvc.psfvcb));
            if (SUCCEEDED(hr))
            {
                CComPtr<IShellView> spsv;
                hr = SHCreateShellFolderView(&sfvc, &spsv);
                if (SUCCEEDED(hr))
                {
                    hr = spsv->QueryInterface(riid, ppv);
                }
                sfvc.psfvcb->Release();
            }
            sfvc.pshf->Release();
        }
    }
    return hr;
}

// figure out a fast way to do this
HRESULT CRegFolder::_HasSubfolders(PCUITEMID_CHILD pidl, __out BOOL *pfHasSubfolders)
{
    *pfHasSubfolders = TRUE;
    return S_OK;
}

HRESULT CRegFolder::_GetAttributes(PCUITEMID_CHILD pidl, DWORD rgfIn, __out DWORD *prgfOut)
{
    BOOL fIsValue;
    HRESULT hr = _IsValue(pidl, &fIsValue);
    if (SUCCEEDED(hr))
    {
        DWORD dwMask = SFGAO_CANRENAME | SFGAO_CANDELETE;
        if (!fIsValue)
        {
            dwMask |= SFGAO_FOLDER | SFGAO_BROWSABLE | SFGAO_CANLINK;
            BOOL fHasSubfolders;
            hr = _HasSubfolders(pidl, &fHasSubfolders);
            if (SUCCEEDED(hr))
            {
                if (fHasSubfolders)
                {
                    dwMask |= SFGAO_HASSUBFOLDER;
                }
            }
        }
        if (SUCCEEDED(hr))
        {
            *prgfOut = rgfIn & dwMask;
        }
    }
    return hr;
}

STDMETHODIMP CRegFolder::GetAttributesOf(UINT cidl, __in_ecount_opt(cidl) PCUITEMID_CHILD_ARRAY rgpidl, __inout SFGAOF *rgfInOut)
{
    HRESULT hr = E_INVALIDARG;
    if (cidl && rgpidl)
    {
        hr = S_OK;
        ULONG rgfOut = *rgfInOut;
        for (UINT i = 0; i < cidl && SUCCEEDED(hr); i++)
        {
            DWORD rgfItem;
            hr = _GetAttributes(rgpidl[i], *rgfInOut, &rgfItem);
            if (SUCCEEDED(hr))
            {
                rgfOut &= rgfItem;
            }
        }
        if (SUCCEEDED(hr))
        {
            *rgfInOut = rgfOut;
        }
    }
    return hr;
}

STDMETHODIMP CRegFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, __in_ecount_opt(cidl) PCUITEMID_CHILD_ARRAY rgpidl, REFIID riid, __reserved UINT *rgfReserved, __deref_out void **ppv)
{
    HRESULT hr = E_NOINTERFACE;
    if (riid == __uuidof(IDataObject))
    {
        hr = SHCreateDataObject(_spidl, cidl, rgpidl, NULL, riid, ppv);
    }
    else if (cidl == 1)
    {
        if (riid == __uuidof(IExtractIcon))
        {
            CComPropVariant spropvarRegType;
            hr = _GetPropertyForItem(rgpidl[0], PKEY_RegistryType, &spropvarRegType);
            if (SUCCEEDED(hr))
            {
                ATLASSERT(spropvarRegType.vt == VT_UI4);

                int iIcon;
                switch (spropvarRegType.uintVal)
                {
                case REG_SZ:
                case REG_MULTI_SZ:
                case REG_EXPAND_SZ:
                    iIcon = -IDI_REGSTRINGVALUE;
                    break;

                case REG_KEY:
                    iIcon = -IDI_REGFOLDER;
                    break;

                case REG_NONE:
                    iIcon = -IDI_NAMESPACEROOT;
                    break;

                default:
                    iIcon = -IDI_REGBINARYVALUE;
                    break;
                }

                CPathString strModulePath;
                if (GetModuleFileName(_AtlBaseModule.GetModuleInstance(), strModulePath, strModulePath.Size()))
                {
                    CComPtr<IDefaultExtractIconInit> spdxi;
                    hr = SHCreateDefaultExtractIcon(IID_PPV_ARGS(&spdxi));
                    if (SUCCEEDED(hr))
                    {
                        // this should not be GIL_PERINSTANCE as that will take up lots of space in the icon
                        // cache. instad this should be per type. but to do that we need to report the type
                        // of the items properly using a PKEY (I need to investigate the details of this)
                        hr = spdxi->SetFlags(GIL_PERINSTANCE);
                        if (SUCCEEDED(hr))
                        {
                            hr = spdxi->SetNormalIcon(strModulePath, iIcon);
                            if (SUCCEEDED(hr))
                            {
                                hr = spdxi->QueryInterface(riid, ppv);
                            }
                        }
                    }
                }
                else
                {
                    hr = AtlHresultFromLastError();
                }
            }
        }
        else if (riid == __uuidof(IQueryAssociations))
        {
            BOOL fIsValue;
            hr = _IsValue(rgpidl[0], &fIsValue);
            if (SUCCEEDED(hr))
            {
                if (fIsValue)
                {
                    hr = E_NOINTERFACE;
                }
                else
                {
                    // this array can be populated with ProgIDs for the different types of
                    // items. That will let the registry provide some of the information about
                    // these items 
                    ASSOCIATIONELEMENT rgAssoc[] = 
                    {
                        { ASSOCCLASS_FOLDER, NULL, NULL },
                        // { ASSOCCLASS_PROGID_STR, NULL, L"RootRegistryKey" };
                        // { ASSOCCLASS_PROGID_STR, NULL, L"RegistryKey" };
                        // { ASSOCCLASS_PROGID_STR, NULL, L"RegistryValue" }; // for fIsValue == TRUE
                    };
                    hr = AssocCreateForClasses(rgAssoc, ARRAYSIZE(rgAssoc), riid, ppv);
                }
            }
        }
        else if (riid == __uuidof(IContextMenu))
        {
            DEFCONTEXTMENU dcm = { hwndOwner, static_cast<IContextMenuCB *>(this), NULL, static_cast<IShellFolder2 *>(this), cidl, rgpidl, NULL, 0, NULL };
            hr = SHCreateDefaultContextMenu(&dcm, riid, ppv);
        }
        else if (riid == __uuidof(IRegValueEditor))
        {
            hr = E_NOINTERFACE;
            CComPtr<IShellItem2> spsi;
            hr = SHCreateItemWithParent(_spidl, static_cast<IShellFolder *>(this), rgpidl[0], IID_PPV_ARGS(&spsi));
            if (SUCCEEDED(hr))
            {
                hr = CreateValueEditor(spsi, riid, ppv);
            }
        }
    }
    return hr;
}

HRESULT CRegFolder::_GetNameDisplay(PCUITEMID_CHILD pidl, __deref_out PWSTR *ppszName)
{
    CComPropVariant spropvar;
    HRESULT hr = _GetPropertyForItem(pidl, PKEY_ItemNameDisplay, &spropvar);
    if (SUCCEEDED(hr))
    {
        *ppszName = spropvar.pwszVal;   // return ownership
        spropvar.vt = VT_EMPTY;
    }
    return hr;
}

HRESULT CRegFolder::_GetAbsoluteName(SIGDN sigdn, PCUITEMID_CHILD pidl, __deref_out PWSTR *ppszName)
{
    PCREGITEM pri;
    HRESULT hr = GetDataFromIDList(pidl, &pri);
    if (SUCCEEDED(hr))
    {
        if (pri->rit == RIT_NAMESPACEROOT && sigdn == SIGDN_DESKTOPABSOLUTEPARSING)
        {
            CComPropVariant spropvar;
            hr = GetPropertyFromIDList(pidl, PKEY_ParsingName, &spropvar);
            if (SUCCEEDED(hr))
            {
                *ppszName = spropvar.pwszVal;
                spropvar.vt = VT_EMPTY;
            }
        }
        else
        {
            CComMemPtr<WCHAR> spszParentName;
            hr = SHGetNameFromIDList(_spidl, sigdn, &spszParentName);
            if (SUCCEEDED(hr))
            {
                CRegistryPathString strName;
                hr = strName.Copy(spszParentName);
                if (SUCCEEDED(hr))
                {
                    if (pri->rit != RIT_NAMESPACEROOT)
                    {
                        hr = strName.Append(L'\\');
                        if (SUCCEEDED(hr))
                        {
                            CComMemPtr<WCHAR> spszChild;
                            hr = SIGDN_DESKTOPABSOLUTEEDITING == sigdn ? _GetNameDisplay(pidl, &spszChild) : _GetParsingName(pidl, &spszChild);
                            if (SUCCEEDED(hr))
                            {
                                hr = strName.Append(spszChild);
                            }
                        }
                    }
                    if (SUCCEEDED(hr))
                    {
                        hr = strName.CoTaskMemCopy(ppszName);
                    }
                }
            }
        }
    }
    return hr;
}

HRESULT CRegFolder::_GetAbsoluteFriendlyName(PCUITEMID_CHILD pidl, __deref_out PWSTR *ppszName)
{
    return _GetAbsoluteName(SIGDN_DESKTOPABSOLUTEEDITING, pidl, ppszName);
}

HRESULT CRegFolder::_GetAbsoluteParsingName(PCUITEMID_CHILD pidl, __deref_out PWSTR *ppszName)
{
    return _GetAbsoluteName(SIGDN_DESKTOPABSOLUTEPARSING, pidl, ppszName);
}

HRESULT CRegFolder::_GetParsingName(PCUITEMID_CHILD pidl, __deref_out PWSTR *ppszName)
{
    CComPropVariant spropvar;
    HRESULT hr = GetPropertyFromIDList(pidl, PKEY_ParsingName, &spropvar);
    if (SUCCEEDED(hr))
    {
        *ppszName = spropvar.pwszVal;
        spropvar.vt = VT_EMPTY;
    }
    return hr;
}

STDMETHODIMP CRegFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, SHGDNF uFlags, __out STRRET *psrName)
{
    PCREGITEM pri;
    HRESULT hr = GetDataFromIDList(pidl, &pri);
    if (SUCCEEDED(hr))
    {
        typedef HRESULT (CRegFolder::*PFNGETDISPLAYNAME)(PCUITEMID_CHILD, PWSTR *);
        static const PFNGETDISPLAYNAME rgpfnGetDisplayName[] = 
        {
            &CRegFolder::_GetNameDisplay,
            &CRegFolder::_GetAbsoluteFriendlyName,
            &CRegFolder::_GetParsingName,
            &CRegFolder::_GetAbsoluteParsingName,
        };

        enum GETDISPLAYNAMEINDEX
        {
            GDNI_RELATIVEFRIENDLY = 0,
            GDNI_ABSOLUTEFRIENDLY,
            GDNI_RELATIVEPARSING,
            GDNI_ABSOLUTEPARSING
        };

        static const BYTE rgFuncIndices[] = {
            //  FOREDITING  FORPARSING  FORADDRESSBAR  INFOLDER
            /*       0           0            0            0    */ GDNI_RELATIVEFRIENDLY,
            /*       0           0            0            1    */ GDNI_RELATIVEFRIENDLY,
            /*       0           0            1            0    */ GDNI_RELATIVEFRIENDLY,
            /*       0           0            1            1    */ GDNI_RELATIVEFRIENDLY,
            /*       0           1            0            0    */ GDNI_ABSOLUTEPARSING,
            /*       0           1            0            1    */ GDNI_RELATIVEPARSING,
            /*       0           1            1            0    */ GDNI_ABSOLUTEFRIENDLY,
            /*       0           1            1            1    */ GDNI_RELATIVEFRIENDLY,
            /*       1           0            0            0    */ GDNI_RELATIVEFRIENDLY,
            /*       1           0            0            1    */ GDNI_RELATIVEFRIENDLY,
            /*       1           0            1            0    */ GDNI_RELATIVEFRIENDLY,
            /*       1           0            1            1    */ GDNI_RELATIVEFRIENDLY,
            /*       1           1            0            0    */ GDNI_ABSOLUTEPARSING,
            /*       1           1            0            1    */ GDNI_RELATIVEPARSING,
            /*       1           1            1            0    */ GDNI_ABSOLUTEFRIENDLY,
            /*       1           1            1            1    */ GDNI_RELATIVEFRIENDLY
        };

        enum GETDISPLAYNAMEMASK
        {
            GDNM_INFOLDER = 0x00000001,
            GDNM_FORADDRESSBAR = 0x00000002,
            GDNM_FORPARSING = 0x00000004,
            GDNM_FOREDITING = 0x00000008
        };

        DWORD dwIndex = 0;
        if (uFlags & SHGDN_INFOLDER)
        {
            dwIndex |= GDNM_INFOLDER;
        }
        if (uFlags & SHGDN_FORPARSING)
        {
            dwIndex |= GDNM_FORPARSING;
        }
        if (uFlags & SHGDN_FORADDRESSBAR)
        {
            dwIndex |= GDNM_FORADDRESSBAR;
        }
        if (uFlags & SHGDN_FOREDITING)
        {
            dwIndex |= GDNM_FOREDITING;
        }

        hr = (this->*(rgpfnGetDisplayName[rgFuncIndices[dwIndex]]))(pidl, &psrName->pOleStr);
        if (SUCCEEDED(hr))
        {
            psrName->uType = STRRET_WSTR;
        }
    }
    return hr;
}

STDMETHODIMP CRegFolder::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCWSTR pszName, SHGDNF uFlags, __deref_out_opt PITEMID_CHILD *ppidlOut)
{
    return E_NOTIMPL;
}

// IShellFolder2
STDMETHODIMP CRegFolder::GetDefaultSearchGUID(__out GUID *pguid)
{
    return E_NOTIMPL;
}

STDMETHODIMP CRegFolder::EnumSearches(__deref_out IEnumExtraSearch **ppenum)
{
    return E_NOTIMPL;
}

STDMETHODIMP CRegFolder::GetDefaultColumn(DWORD dwRes, __out ULONG *plSort, __out ULONG *plDisplay)
{
    *plSort = 0;
    *plDisplay = 0;
    return S_OK;
}

HRESULT CRegFolder::_ColumnStateFromPropDesc(__in IPropertyDescription *ppropdesc, __out SHCOLSTATEF *pcsFlags)
{
    SHCOLSTATEF cs;
    HRESULT hr = ppropdesc->GetColumnState(&cs);
    if (SUCCEEDED(hr))
    {
        PROPDESC_TYPE_FLAGS dtf;
        hr = ppropdesc->GetTypeFlags(PDTF_ISVIEWABLE, &dtf);
        if (SUCCEEDED(hr))
        {
            if (!(dtf & PDTF_ISVIEWABLE))
            {
                cs |= SHCOLSTATE_HIDDEN;
            }
            PROPDESC_VIEW_FLAGS dvf;
            hr = ppropdesc->GetViewFlags(&dvf);
            if (SUCCEEDED(hr))
            {
                if (dvf & PDVF_SHOWBYDEFAULT)
                {
                    cs |= SHCOLSTATE_ONBYDEFAULT;
                }
                if (dvf & PDVF_SHOWINSECONDARYLIST)
                {
                    cs |= SHCOLSTATE_SECONDARYUI;
                }
                *pcsFlags = cs;
            }
        }
    }
    return hr;
}

STDMETHODIMP CRegFolder::GetDefaultColumnState(UINT iColumn, __out SHCOLSTATEF *pcsFlags)
{
    HRESULT hr = E_INVALIDARG;
    if (iColumn < ARRAYSIZE(c_rgColumnInfo))
    {
        CComPtr<IPropertyDescription> sppropdesc;
        hr = PSGetPropertyDescription(c_rgColumnInfo[iColumn].key, IID_PPV_ARGS(&sppropdesc));
        if (SUCCEEDED(hr))
        {
            hr = _ColumnStateFromPropDesc(sppropdesc, pcsFlags);
            if (SUCCEEDED(hr))
            {
                *pcsFlags |= c_rgColumnInfo[iColumn].scsf;
            }
        }
    }
    return hr;
}

STDMETHODIMP CRegFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const PROPERTYKEY *pkey, __out VARIANT *pvar)
{
    CComPropVariant spropvar;
    HRESULT hr = _GetPropertyForItem(pidl, *pkey, &spropvar);
    if (SUCCEEDED(hr))
    {
        hr = PropVariantToVariant(&spropvar, pvar);
    }
    return hr;
}

HRESULT CRegFolder::_GetListViewFlagsFromPropDesc(__in IPropertyDescription *ppropdesc, __out int *pfmt)
{
    int fmt = LVCFMT_LEFT;          // default
    PROPDESC_VIEW_FLAGS dvf;
    HRESULT hr = ppropdesc->GetViewFlags(&dvf);
    if (SUCCEEDED(hr))
    {
        // Handle the mutually exclusive part
        if (dvf & PDVF_RIGHTALIGN)
        {
            fmt = LVCFMT_RIGHT;
        }
        else if (dvf & PDVF_CENTERALIGN)
        {
            fmt = LVCFMT_CENTER;
        }

        static const struct PROPVIEWFLAGSTOLVCFMT
        {
            PROPDESC_VIEW_FLAGS dvf;
            int fmt;
        }
        s_mapFlags[] = 
        {
            { PDVF_BEGINNEWGROUP, LVCFMT_LINE_BREAK },
            { PDVF_FILLAREA, LVCFMT_FILL },
            { PDVF_HIDELABEL, LVCFMT_NO_TITLE },
            { PDVF_CANWRAP, LVCFMT_WRAP },
        };

        for (int i = 0; i < ARRAYSIZE(s_mapFlags); i++)
        {
            if (dvf & s_mapFlags[i].dvf)
            {
                fmt |= s_mapFlags[i].fmt;
            }
        }
        *pfmt = fmt;
    }
    return hr;
}

HRESULT CRegFolder::_ShellDetailsFromPropDesc(__in IPropertyDescription *ppropdesc, __out SHELLDETAILS *psd)
{
    HRESULT hr = _GetListViewFlagsFromPropDesc(ppropdesc, &psd->fmt);
    if (SUCCEEDED(hr))
    {
        hr = ppropdesc->GetDefaultColumnWidth((UINT *)&psd->cxChar);
        if (SUCCEEDED(hr))
        {
            PROPDESC_TYPE_FLAGS dtf;
            hr = ppropdesc->GetTypeFlags(PDTF_ISVIEWABLE, &dtf);
            if (SUCCEEDED(hr))
            {
                CComMemPtr<WCHAR> spszDisplayName;
                hr = ppropdesc->GetDisplayName(&spszDisplayName);
                if (FAILED(hr) && !(dtf & PDTF_ISVIEWABLE))
                {
                    // Hidden columns don't have to have a display name in the schema
                    hr = SHStrDup(L"", &spszDisplayName);
                }

                if (SUCCEEDED(hr))
                {
                    psd->str.uType = STRRET_WSTR;
                    psd->str.pOleStr = spszDisplayName.Detach();
                }
            }
        }
    }
    return hr;
}

STDMETHODIMP CRegFolder::GetDetailsOf(__in_opt PCUITEMID_CHILD pidl, UINT iColumn, __out SHELLDETAILS *psd)
{
    HRESULT hr = E_INVALIDARG;
    if (iColumn < ARRAYSIZE(c_rgColumnInfo))
    {
        if (pidl)
        {
            CComPropVariant spropvar;
            hr = _GetPropertyForItem(pidl, c_rgColumnInfo[iColumn].key, &spropvar);
            if (SUCCEEDED(hr))
            {
                hr = spropvar.ChangeType(VT_LPWSTR);
                if (SUCCEEDED(hr))
                {
                    psd->str.uType = STRRET_WSTR;
                    psd->str.pOleStr = spropvar.pwszVal;
                    spropvar.vt = VT_EMPTY;
                }
            }
        }
        else
        {
            CComPtr<IPropertyDescription> sppropdesc;
            hr = PSGetPropertyDescription(c_rgColumnInfo[iColumn].key, IID_PPV_ARGS(&sppropdesc));
            if (SUCCEEDED(hr))
            {
                hr = _ShellDetailsFromPropDesc(sppropdesc, psd);
            }
        }
    }
    return hr;
}

STDMETHODIMP CRegFolder::MapColumnToSCID(UINT iColumn, __out PROPERTYKEY *pkey)
{
    HRESULT hr = E_INVALIDARG;
    if (iColumn < ARRAYSIZE(c_rgColumnInfo))
    {
        *pkey = c_rgColumnInfo[iColumn].key;
        hr = S_OK;
    }
    return hr;
}

// IRegPropertyHandler
STDMETHODIMP CRegFolder::GetProperty(PCUITEMID_CHILD pidl, REFPROPERTYKEY key, __out PROPVARIANT *ppropvar)
{
    return _GetPropertyForItem(pidl, key, ppropvar);
}

STDMETHODIMP CRegFolder::SetProperty(PCUITEMID_CHILD pidl, REFPROPERTYKEY key, REFPROPVARIANT propvar)
{
    HRESULT hr = E_INVALIDARG;
    if (IsEqualPropertyKey(key, PKEY_RegistryData))
    {
        CComPropVariant spropvarRegType;
        hr = _GetPropertyForItem(pidl, PKEY_RegistryType, &spropvarRegType);
        if (SUCCEEDED(hr))
        {
            ATLASSERT(spropvarRegType.vt == VT_UI4);
            CComPropVariant spropvarName;
            hr = _GetPropertyForItem(pidl, PKEY_ItemNameDisplay, &spropvarName);
            if (SUCCEEDED(hr))
            {
                CRegKey rk;
                hr = AtlHresultFromWin32(_OpenKeyForItem(pidl, KEY_WRITE, &rk));
                if (SUCCEEDED(hr))
                {
                    switch (spropvarRegType.uintVal)
                    {
                    case REG_SZ:
                        hr = E_INVALIDARG;
                        if (propvar.vt == VT_LPWSTR || propvar.vt == VT_BSTR)
                        {
                            hr = AtlHresultFromWin32(rk.SetStringValue(spropvarName.pwszVal, propvar.pwszVal));
                        }
                        break;

                    case REG_MULTI_SZ:
                        hr = E_INVALIDARG;
                        if (propvar.vt == VT_BLOB)
                        {
                            hr = AtlHresultFromWin32(rk.SetMultiStringValue(spropvarName.pwszVal, (PCWSTR)propvar.blob.pBlobData));
                        }
                        break;

                    case REG_DWORD:
                        hr = E_INVALIDARG;
                        if (propvar.vt == VT_UI4)
                        {
                            hr = AtlHresultFromWin32(rk.SetDWORDValue(spropvarName.pwszVal, propvar.ulVal));
                        }
                        break;

                    case REG_BINARY:
                        hr = E_INVALIDARG;
                        if (propvar.vt == VT_BLOB)
                        {
                            hr = AtlHresultFromWin32(rk.SetBinaryValue(spropvarName.pwszVal, propvar.blob.pBlobData, propvar.blob.cbSize));
                        }
                        break;

                    default:
                        ATLASSERT(FALSE);   // Should handle all types necessary
                        break;

                    }

                    if (SUCCEEDED(hr))
                    {
                        CComMemPtr<ITEMIDLIST_ABSOLUTE> spidlAbsolute;
                        hr = CombineIDLists(reinterpret_cast<PCIDLIST_ABSOLUTE>((PIDLIST_RELATIVE)_spidl), reinterpret_cast<PCIDLIST_ABSOLUTE>((PIDLIST_RELATIVE)pidl), reinterpret_cast<PIDLIST_ABSOLUTE *>(&spidlAbsolute));
                        if (SUCCEEDED(hr))
                        {
                            SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_IDLIST | SHCNF_FLUSH, spidlAbsolute, NULL);
                        }
                    }
                }
            }
        }
    }
    return hr;
}

// IContextMenuCB
HRESULT CRegFolder::_CmdIDToMap(UINT_PTR uiCmdId, BOOL fUnicode, const CMVERBTOIDMAP *pmap, UINT cVerbs, __out const CMVERBTOIDMAP **ppmapMatch)
{
    HRESULT hr = E_FAIL;
    if (IS_INTRESOURCE(uiCmdId))
    {
        for (UINT i = 0; i < cVerbs && FAILED(hr); i++, pmap++)
        {
            if (pmap->uiCmdId == uiCmdId)
            {
                *ppmapMatch = pmap;
                hr = S_OK;
            }
        }
    }
    else if (fUnicode)
    {
        PCWSTR pszVerb = (PCWSTR)uiCmdId;
        for (UINT i = 0; i < cVerbs && FAILED(hr); i++, pmap++)
        {
            if (lstrcmp(pmap->pszVerb, pszVerb) == 0)
            {
                *ppmapMatch = pmap;
                hr = S_OK;
            }
        }
    }
    else
    {
        LPCSTR pszVerbA = (LPCSTR)uiCmdId;
        for (UINT i = 0; i < cVerbs && FAILED(hr); i++, pmap++)
        {
            if (lstrcmpA(pmap->pszVerbA, pszVerbA) == 0)
            {
                *ppmapMatch = pmap;
                hr = S_OK;
            }
        }
    }
    return hr;
}

HRESULT CRegFolder::_MapVerbToCmdID(PCWSTR pszVerb, const CMVERBTOIDMAP *pmap, UINT cVerbs, __out UINT *puiCmdId)
{
    *puiCmdId = -1;
    const CMVERBTOIDMAP *pmapMatch;
    HRESULT hr = _CmdIDToMap((UINT_PTR)pszVerb, TRUE, pmap, cVerbs, &pmapMatch);
    if (SUCCEEDED(hr))
    {
        *puiCmdId = pmapMatch->uiCmdId;
    }
    return hr;
}

HRESULT CRegFolder::_MapCmdIDToVerb(UINT_PTR uiCmdId, UINT uiFlags, const CMVERBTOIDMAP *pmap, UINT cVerbs, __out_awcount(!(uiFlags & GCS_UNICODE), cch) LPSTR pszName, UINT cch)
{
    const CMVERBTOIDMAP *pmapMatch;
    HRESULT hr = _CmdIDToMap(uiCmdId, uiFlags & GCS_UNICODE, pmap, cVerbs, &pmapMatch);
    if (SUCCEEDED(hr))
    {
        if (uiFlags & GCS_UNICODE)
        {
            hr = StringCchCopyW((LPWSTR)pszName, cch, pmapMatch->pszVerb);
        }
        else
        {
            hr = StringCchCopyA(pszName, cch, pmapMatch->pszVerbA);
        }
    }
    return hr;
}

HRESULT CRegFolder::_MapCmdIDToHelpText(UINT_PTR uiCmdId, UINT uiFlags, const CMVERBTOIDMAP *pmap, UINT cVerbs, __out_awcount(!(uiFlags & GCS_UNICODE), cch) LPSTR pszName, UINT cch)
{
    const CMVERBTOIDMAP *pmapMatch;
    HRESULT hr = _CmdIDToMap(uiCmdId, uiFlags & GCS_UNICODE, pmap, cVerbs, &pmapMatch);
    if (SUCCEEDED(hr))
    {
        if (uiFlags & GCS_UNICODE)
        {
            LoadString(_AtlBaseModule.GetModuleInstance(), pmapMatch->uiHelpId, (LPWSTR)pszName, cch);
        }
        else
        {
            LoadStringA(_AtlBaseModule.GetModuleInstance(), pmapMatch->uiHelpId, pszName, cch);
        }
    }
    return hr;
}

HRESULT CRegFolder::_ValidateCmdID(UINT_PTR uiCmdId, UINT uiFlags, const CMVERBTOIDMAP *pmap, UINT cVerbs)
{
    const CMVERBTOIDMAP *pmapMatch;
    return _CmdIDToMap(uiCmdId, uiFlags & GCS_UNICODE, pmap, cVerbs, &pmapMatch);
}

HRESULT CRegFolder::_HandleStandardMenuMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam, const CMVERBTOIDMAP *pmap, UINT cVerbs)
{
    HRESULT hr;

    switch (uiMsg)
    {
    case DFM_MAPCOMMANDNAME:
        hr = _MapVerbToCmdID((PCWSTR)lParam, pmap, cVerbs, (UINT *)wParam);
        break;

    case DFM_GETVERBW:
        hr = _MapCmdIDToVerb(LOWORD(wParam), GCS_VERBW, pmap, cVerbs, (LPSTR)lParam, HIWORD(wParam));
        break;
    case DFM_GETVERBA:
        hr = _MapCmdIDToVerb(LOWORD(wParam), GCS_VERBA, pmap, cVerbs, (LPSTR)lParam, HIWORD(wParam));
        break;

    case DFM_GETHELPTEXTW:
        hr = _MapCmdIDToHelpText(LOWORD(wParam), GCS_HELPTEXTW, pmap, cVerbs, (LPSTR)lParam, HIWORD(wParam));
        break;
    case DFM_GETHELPTEXT:
        hr = _MapCmdIDToHelpText(LOWORD(wParam), GCS_HELPTEXTA, pmap, cVerbs, (LPSTR)lParam, HIWORD(wParam));
        break;

    case DFM_VALIDATECMD:
        hr = _ValidateCmdID(wParam, GCS_VALIDATEW, pmap, cVerbs);
        break;

    default:
        hr = E_NOTIMPL;
        break;
    }

    if (FAILED(hr) && (E_NOTIMPL != hr) && (DFM_MAPCOMMANDNAME != uiMsg))
    {
        hr = S_FALSE; // not ours, must be a standard verb, let defcm handle it for us
    }

    return hr;
}

HRESULT _GetFirstItemInDataObject(IDataObject *pdo, REFIID riid, void **ppv)
{
    *ppv = NULL;

    CComPtr<IShellItemArray> spsia;
    HRESULT hr = SHCreateShellItemArrayFromDataObject(pdo, IID_PPV_ARGS(&spsia));
    if (SUCCEEDED(hr))
    {
        CComPtr<IShellItem> spsi;
        hr = spsia->GetItemAt(0, &spsi);
        if (SUCCEEDED(hr))
        {
            hr = spsi->QueryInterface(riid, ppv);
        }
    }
    return hr;
}

STDMETHODIMP CRegFolder::CallBack(__in_opt IShellFolder *psf, HWND hwndOwner, __in_opt IDataObject *pdo, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
    static const CMVERBTOIDMAP s_rgMenuMap[] = 
    {
        { L"edit", "edit", IDM_EDITVALUE, -1 },
    };

    HRESULT hr = S_OK;
    switch (uiMsg)
    {
    case DFM_MERGECONTEXTMENU:
        {
            CComPtr<IShellItem2> spsi;
            hr = _GetFirstItemInDataObject(pdo, IID_PPV_ARGS(&spsi));
            if (SUCCEEDED(hr))
            {
                int iRegType;
                hr = spsi->GetInt32(PKEY_RegistryType, &iRegType);
                if (SUCCEEDED(hr) && (iRegType != REG_KEY))
                {
                    QCMINFO *pqcmi = (QCMINFO *)lParam;
                    HMENU hmnuMerge;
                    hr = LoadPopupMenu(_AtlBaseModule.GetModuleInstance(), IDM_VALUE_MERGE, &hmnuMerge);
                    if (SUCCEEDED(hr))
                    {
                        UINT uiCmdIdFirst = pqcmi->idCmdFirst;
                        UINT uiMax = Shell_MergeMenus(pqcmi->hmenu, hmnuMerge, pqcmi->indexMenu, pqcmi->idCmdFirst, pqcmi->idCmdLast,
                                                      MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS | MM_DONTREMOVESEPS);
                        DestroyMenu(hmnuMerge);
                        pqcmi->idCmdFirst = uiMax;
                        if (!(wParam & CMF_NODEFAULT))
                        {
                            SetMenuDefaultItem(pqcmi->hmenu, uiCmdIdFirst + IDM_EDITVALUE, MF_BYCOMMAND);
                        }
                    }
                }
            }
        }
        break;

    case DFM_INVOKECOMMANDEX:
        switch (wParam)
        {
        case IDM_EDITVALUE:
            {
                CComPtr<IShellItem> spsi;
                if (SUCCEEDED(_GetFirstItemInDataObject(pdo, IID_PPV_ARGS(&spsi))))
                {
                    CComPtr<IRegValueEditor> sprve;
                    hr = spsi->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARGS(&sprve));
                    if (SUCCEEDED(hr))
                    {
                        sprve->EditModal(hwndOwner);
                    }
                }
            }
            break;

        default:
            hr = S_FALSE;   // do the default "Create Shortcut" for example
            break;
        }
        break;

    case DFM_MAPCOMMANDNAME:
    case DFM_GETVERBW:
    case DFM_GETVERBA:
    case DFM_GETHELPTEXTW:
    case DFM_GETHELPTEXT:
    case DFM_VALIDATECMD:
        hr = _HandleStandardMenuMessage(uiMsg, wParam, lParam, s_rgMenuMap, ARRAYSIZE(s_rgMenuMap));
        break;

    default:
        hr = E_NOTIMPL;
        break;
    }

    return hr;
}

